package com.ruoyi.caiwu.service.impl;

import com.ruoyi.caiwu.domain.AccountingVoucher;
import com.ruoyi.caiwu.domain.SubjectBalance;
import com.ruoyi.caiwu.domain.vo.AccountingSubjectsVo;
import com.ruoyi.caiwu.domain.vo.AccountingVoucherVo;
import com.ruoyi.caiwu.mapper.AccountingSubjectsMapper;
import com.ruoyi.caiwu.mapper.AccountingVoucherMapper;
import com.ruoyi.caiwu.mapper.SubjectBalanceMapper;
import com.ruoyi.caiwu.service.SubjectBalanceService;
import com.ruoyi.common.core.web.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * @description: 科目余额服务实现类，负责处理科目余额相关的业务逻辑
 * @author: 杜炳坤
 * @date: 2025/3/6 14:02
 * @version: 1.0
 */
@Service
public class SubjectBalanceServiceImpl implements SubjectBalanceService {

    // 注入科目余额表的 Mapper，用于操作科目余额表数据库
    @Autowired
    private SubjectBalanceMapper subjectBalanceMapper;

    // 注入凭证表的 Mapper，用于操作凭证表数据库
    @Autowired
    private AccountingVoucherMapper accountingVoucherMapper;

    // 注入科目表的 Mapper，用于操作科目表数据库
    @Autowired
    private AccountingSubjectsMapper accountingSubjectsMapper;

    /**
     * 插入或更新科目余额记录
     *
     * @param voucherDate 凭证日期
     * @param subjectId   科目 ID
     * @return 包含操作结果的 AjaxResult 对象
     */
    @Override
    @Transactional
    public AjaxResult insertUpdateSubjectBalance(Date voucherDate, Long subjectId) {
        // 获取凭证日期对应的年份，注意：Date 类的 getYear() 方法返回的是相对于 1900 年的差值，所以要加 1900
        int year = voucherDate.getYear() + 1900;
        // 获取凭证日期对应的月份，注意：Date 类的 getMonth() 方法返回的月份是从 0 开始的，所以要加 1
        int month = voucherDate.getMonth() + 1;
        // 根据科目 ID 查询科目名称及编号
        AccountingSubjectsVo nameById = accountingSubjectsMapper.getNameById(subjectId);
        // 根据年份、月份和科目编号查询科目余额表中是否存在对应记录，返回记录的 ID
        Integer id = accountingSubjectsMapper.selectAccountingSubjectsId(year, month, nameById.getSubjectCode());
        // 如果 ID 不为空，说明该月已有相同科目的记录，调用更新方法；否则调用插入方法
        return id != null ? updateSubjectBalance(id, voucherDate, subjectId) : insertSubjectBalance(voucherDate, subjectId);
    }

    /**
     * 查询科目余额表的所有数据
     *
     * @return 包含科目余额列表的 AjaxResult 对象
     */
    @Override
    public AjaxResult selectSubjectBalance() {
        // 调用 Mapper 方法查询科目余额表的所有数据
        List<SubjectBalance> subjectBalanceList = subjectBalanceMapper.selectSubjectBalance();
        // 将查询结果封装到 AjaxResult 中返回
        return AjaxResult.success(subjectBalanceList);
    }

    /**
     * 更新科目余额记录
     *
     * @param id          科目余额记录的 ID
     * @param voucherDate 凭证日期
     * @param subjectId   科目 ID
     * @return 包含操作结果的 AjaxResult 对象
     */
    private AjaxResult updateSubjectBalance(Integer id, Date voucherDate, Long subjectId) {
        // 调用 buildSubjectBalance 方法构建科目余额对象
        SubjectBalance subjectBalance = buildSubjectBalance(voucherDate, subjectId);
        // 设置科目余额记录的 ID
        subjectBalance.setId(id);
        // 调用 Mapper 方法更新科目余额记录，返回受影响的行数
        Integer result = subjectBalanceMapper.updateSubjectBalance(subjectBalance);
        // 根据受影响的行数判断操作是否成功，成功则返回成功的 AjaxResult，失败则返回错误的 AjaxResult
        return result > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /**
     * 插入科目余额记录
     *
     * @param voucherDate 凭证日期
     * @param subjectId   科目 ID
     * @return 包含操作结果的 AjaxResult 对象
     */
    private AjaxResult insertSubjectBalance(Date voucherDate, Long subjectId) {
        // 调用 buildSubjectBalance 方法构建科目余额对象
        SubjectBalance subjectBalance = buildSubjectBalance(voucherDate, subjectId);
        // 调用 Mapper 方法插入科目余额记录，返回受影响的行数
        Integer result = subjectBalanceMapper.insertSubjectBalance(subjectBalance);
        // 根据受影响的行数判断操作是否成功，成功则返回成功的 AjaxResult，失败则返回错误的 AjaxResult
        return result > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /**
     * 构建科目余额对象
     *
     * @param voucherDate 凭证日期
     * @param subjectId   科目 ID
     * @return 构建好的科目余额对象
     */
    private SubjectBalance buildSubjectBalance(Date voucherDate, Long subjectId) {
        // 从凭证表中查询科目初始金额
        AccountingVoucherVo subjectInitialAmount = accountingVoucherMapper.checkInitialAmount(voucherDate, subjectId);
        // 从凭证表中查询科目当前余额
        AccountingVoucherVo getAccountBalance = accountingVoucherMapper.captureAccountBalance(voucherDate, subjectId);
        // 从凭证表中查询科目本年累计金额
        AccountingVoucherVo cumulativeAmountThisYear = accountingVoucherMapper.checkAnnualCreditAmount(voucherDate, subjectId);
        // 从凭证表中查询科目本期累计余额
        AccountingVoucherVo accountBalanceForTheMonth = accountingVoucherMapper.checkMonthlyCreditAmount(voucherDate, subjectId);
        // 根据科目 ID 查询科目名称及编号
        AccountingSubjectsVo accountingSubjectsVo = accountingSubjectsMapper.getNameById(subjectId);

        // 创建科目余额对象
        SubjectBalance subjectBalance = new SubjectBalance();
        // 设置科目期初借方余额
        subjectBalance.setInitialDebit(subjectInitialAmount.getInitialDebitAmount());
        // 设置科目期初贷方余额
        subjectBalance.setInitialCredit(subjectInitialAmount.getInitialCreditAmount());
        // 设置科目期末借方余额
        subjectBalance.setEndingDebit(accountBalanceForTheMonth.getMonthlyDebitAmount() - accountBalanceForTheMonth.getMonthlyCreditAmount());
        // 设置科目期末贷方余额
        subjectBalance.setEndingCredit(getAccountBalance.getCurrentCreditAmount());
        // 设置科目本年累计借方发生额
        subjectBalance.setYearToDateDebit(cumulativeAmountThisYear.getDebitAmountForYear());
        // 设置科目本年累计贷方发生额
        subjectBalance.setYearToDateCredit(cumulativeAmountThisYear.getAnnualCreditAmount());
        // 设置科目本期借方发生额
        subjectBalance.setCurrentDebit(accountBalanceForTheMonth.getMonthlyDebitAmount());
        // 设置科目本期贷方发生额
        subjectBalance.setCurrentCredit(accountBalanceForTheMonth.getMonthlyCreditAmount());
        // 设置科目编码
        subjectBalance.setSubjectCode(accountingSubjectsVo.getSubjectCode());
        // 设置科目名称
        subjectBalance.setSubjectName(accountingSubjectsVo.getSubjectName());
        // 设置科目余额表创建时间的会计年度
        subjectBalance.setAccountingYear(voucherDate.getYear() + 1900);
        // 设置科目余额表创建时间的会计期间
        subjectBalance.setAccountingPeriod(voucherDate.getMonth() + 1);
        return subjectBalance;
    }
}

